#pragma once
#include <list>
#include <vector>
#include <string>
#include <iostream>
using namespace std;

#include "Vertex.h"
#include "Edge.h"


enum EdgeTypeLoad	{FREE = -182182181, CONNECTED};
enum DFSType		{CYCLICAL, NOT_CYCLICAL};
#define				INFINITE_INT  2147483646

class MyDirectX;

class GraphFather
{
protected: // private data
	Vertex*				God;		// first vertex(number zero) in graph
	list <Vertex*>		vertexes;
	list <Edge*>		edges;

	bool				Directed;

public:

	GraphFather (bool directed)
	{
		God				= new Vertex();
		vertexes.push_back(God);
		Directed		= directed;
	}

/*---------------------------------------------------- -------------- ----------------------------*/
/*-------------BAIZOR--------------------------------- Data Functions ----------------------------*/
/*---------------------------------------------------- -------------- ----------------------------*/
	void						clear() // test: none
	{
		list <Vertex*>::iterator	It	= vertexes.begin();

		for(int q=0; q<vertexes.size(); q++, It++)
			delete *It;

		list <Edge*>::iterator		It2	= edges.begin();

		for(int q=0; q<edges.size(); q++, It2++)
			delete *It2;

		vertexes.clear();
		edges.clear();
		God = NULL;
	}

	bool						readFromFile_Matrix(string &file_name) // test: error
	{
		clear();

		FILE			*MatrixFile = fopen(file_name.c_str(), "r");
		
		int				height,
						width;

		if(fscanf			(MatrixFile, "%d %d", &height, &width) == 0)
		{
			fclose	(MatrixFile);
			return false;
		}

		if(height <= 0 || width <= 0)
		{
			fclose	(MatrixFile);
			return false;
		}


		vector<Vertex*>	new_Vertexes(height);

		for(int q=0; q<height; q++)
			new_Vertexes[q] = new Vertex();

		for(int q=0; q<height; q++)
		{
			for(int w=0; w<width; w++)
			{
				float	f_buffer;
				if(fscanf	(MatrixFile, "%f", &f_buffer) == 0)
				{
					fclose	(MatrixFile);

					for(int e=0; e<height; e++)
						delete new_Vertexes[q];

					new_Vertexes.clear();
					return false;
				}

				if (q == w)
					continue;

				if (!Directed && w > q)
					continue;

				if(f_buffer != FREE)
				{
					Edge	*new_Edge = new Edge (new_Vertexes[q], f_buffer);
					
					new_Edge -> addVertex	(new_Vertexes[w]);
					edges.push_back			(new_Edge);

					new_Vertexes[q] -> addEdge (new_Edge);

					if (!Directed)
						new_Vertexes[w] -> addEdge (new_Edge);
				}
				
			}

			vertexes.push_back (new_Vertexes[q]);
		}

		God = new_Vertexes[0];
		new_Vertexes.clear ();

		fclose (MatrixFile);

		return true;
	}
	bool						readFromStdInput_Matrix()
	{
		clear();

		int				height,
						width;

		if(scanf			("%d %d", &height, &width) == 0)
			return false;

		if(height <= 0 || width <= 0)
			return false;


		vector<Vertex*>	new_Vertexes(height);

		for(int q=0; q<height; q++)
			new_Vertexes[q] = new Vertex();

		for(int q=0; q<height; q++)
		{
			for(int w=0; w<width; w++)
			{
				float	f_buffer;
				if(scanf	("%f", &f_buffer) == 0)
				{
					for(int e=0; e<height; e++)
						delete new_Vertexes[q];

					new_Vertexes.clear();
					return false;
				}

				if (q == w)
					continue;

				if (!Directed && w > q)
					continue;

				if(f_buffer != FREE)
				{
					Edge	*new_Edge = new Edge (new_Vertexes[q], f_buffer);
					
					new_Edge -> addVertex	(new_Vertexes[w]);
					edges.push_back			(new_Edge);

					new_Vertexes[q] -> addEdge (new_Edge);

					if (!Directed)
						new_Vertexes[w] -> addEdge (new_Edge);
				}
			}
			vertexes.push_back (new_Vertexes[q]);
		}

		God = new_Vertexes[0];
		new_Vertexes.clear ();

		return true;
	}
	bool						readFromFile_Edges_List(string &file_name)
	{
		clear();
		
		FILE	*List	=	fopen(file_name.c_str(), "r");

		int				vertexes_Count,
						edges_Count;

		if(fscanf			(List, "%d %d", &vertexes_Count, &edges_Count) == 0)
		{
			fclose (List);
			return false;
		}

		if(vertexes_Count <= 0 || edges_Count <= 0)
		{
			fclose (List);
			return false;
		}


		vector<Vertex*>	new_Vertexes(vertexes_Count);

		for(int q=0; q<vertexes_Count; q++)
		{
			new_Vertexes[q] = new Vertex();
			vertexes.push_back (new_Vertexes[q]);
		}

		for(int q=0; q<edges_Count; q++)
		{
			int		index1,
					index2;

			fscanf (List, "%d %d", &index1, &index2);

			index1--;
			index2--;
			
			Edge	*new_Edge = new Edge (new_Vertexes[index2], 1);
					
			new_Edge -> addVertex	(new_Vertexes[index1]);
			edges.push_back			(new_Edge);

			new_Vertexes[index2] -> addEdge (new_Edge);

			if (!Directed)
				new_Vertexes[index1] -> addEdge (new_Edge);
		}


		God = new_Vertexes[0];
		new_Vertexes.clear ();

		fclose (List);

		return true;
	}
	bool						readFromStdInput_Edges_List()
	{
		clear();

		int				vertexes_Count,
						edges_Count;

		if(scanf			("%d %d", &vertexes_Count, &edges_Count) == 0)
			return false;

		if(vertexes_Count <= 0 || edges_Count <= 0)
			return false;


		vector<Vertex*>	new_Vertexes(vertexes_Count);

		for(int q=0; q<vertexes_Count; q++)
		{
			new_Vertexes[q] = new Vertex();
			vertexes.push_back (new_Vertexes[q]);
		}

		for(int q=0; q<edges_Count; q++)
		{
			int		index1,
					index2;

			scanf ("%d %d", &index1, &index2);

			index1--;
			index2--;
			
			Edge	*new_Edge = new Edge (new_Vertexes[index2], 1);
					
			new_Edge -> addVertex	(new_Vertexes[index1]);
			edges.push_back			(new_Edge);

			new_Vertexes[index2] -> addEdge (new_Edge);

			if (!Directed)
				new_Vertexes[index1] -> addEdge (new_Edge);
		}


		God = new_Vertexes[0];
		new_Vertexes.clear ();

		return true;
	}
	void						numericAllVertexes() // test: good
	{
		list <Vertex*> :: iterator It = vertexes.begin();

		for(int q=0; q<vertexes.size(); q++, It++)
			(*It) -> setName(q+1);
	}
	void						clearAllVertexesType() // test: none
	{
		list <Vertex*> :: iterator It = vertexes.begin();

		for(int q=0; q<vertexes.size(); q++, It++)
			(*It) -> Type = CLEAR;
	}
	int							vertexesCount()
	{
		return vertexes.size();
	}
	int							edgesCount()
	{
		return edges.size();
	}
	list <Vertex*> :: iterator	begin_Vertex()
	{
		return vertexes.begin();
	}
	list <Vertex*> :: iterator	end_Vertex()
	{
		return vertexes.end();
	}
	list <Edge*>   :: iterator  begin_Edge()
	{
		return edges.begin();
	}
	list <Edge*>   :: iterator	end_Edge()
	{
		return edges.end();
	}
	Vertex*						getGod()
	{
		return God;
	}



/*---------------------------------------------------- ------------- -----------------------------*/
/*-------------BAIZOR--------------------------------- 3D Modulation -----------------------------*/
/*---------------------------------------------------- ------------- -----------------------------*/

	void Show_3D_Model();
	void Generate_3D_Model(MyDirectX &device);


/*---------------------------------------------------- -------------------- ----------------------*/
/*-------------BAIZOR--------------------------------- Algorithms Functions ----------------------*/
/*---------------------------------------------------- -------------------- ----------------------*/
	Edge*		getMinimumEdge() // test: none
	{
		list <Edge*> :: iterator	minimum_W_Iterator	= edges.begin(),
									It					= edges.begin();

		for(int q=0; q<edges.size(); q++, It++)
		{
			if( *minimum_W_Iterator > *It )
				minimum_W_Iterator	= It;
		}

		return (*minimum_W_Iterator);
	}
	Edge*		getMinimumEdgeFromVertex(Vertex* vertex) // bad logic!!! WARNING!!!
	{
		Edge		*Minimum = NULL;
		Vertex		*Friend  = vertex -> goTo(0, Directed);

		for(int q=1; Friend != NULL; q++)
		{
			if (inVertexesList (Friend) == -1)
			{
				Edge	*newEdge = Friend -> getEdgeWhoGoTo(vertex); //getLastUseEdge ();
				if( Minimum == NULL || newEdge != NULL && *Minimum > *newEdge)
					Minimum = newEdge;
			}
			Friend = vertex -> goTo(q, Directed);
		}

		return Minimum;
	}
	Edge*		getMinimumEdgeFromVertexFriends()
	{
		list <Vertex*> :: iterator It = vertexes.begin();

		Edge		*result = getMinimumEdgeFromVertex(*It);
		
		It++;

		for(int q=1; q<vertexes.size(); q++, It++)
		{
			Edge	*Minimum = getMinimumEdgeFromVertex(*It);

			if(result == NULL || Minimum != NULL && *result > *Minimum)
				result = Minimum;
		}

		return result;
	}
	int			inVertexesList(Vertex* search)
	{
		list <Vertex*> :: iterator It = vertexes.begin();

		for(int q=0; q<vertexes.size(); q++, It++)
		{
			if (search == *It)
				return q;
		}

		return -1;
	}	
};


// -=BAIZOR=-